#include "xlsreader.h"
#include <iostream>
#include <sstream>

XlsReader::XlsReader()
{
    m_pRecord = NULL;
    m_pSchema = NULL;
    m_pConnection = NULL;
}

XlsReader::~XlsReader()
{
    CleanUp();
}

bool XlsReader::Eof()
{
    if( !m_pRecord )
        return true;
    if (m_pRecord->adoEOF==0)
        return false;
    return true;
}

bool XlsReader::LoadXlsSheet( std::string name,std::string sheetname, bool header /*= true */ )
{
    CleanUp();

    if(FAILED(::CoInitialize(NULL))) 
        return false;
    WIN32_FIND_DATAA fd;
    if( INVALID_HANDLE_VALUE  == ::FindFirstFileA(name.c_str(), &fd) )
        return false;

    int cellCount = 0;

    try
    {
        _bstr_t connStr(makeConnStr(name, header).c_str());
        
        TESTHR(m_pConnection.CreateInstance(__uuidof(Connection)));
        TESTHR(m_pConnection->Open(connStr, "", "", NULL));
        
        sheetname = sheetname + "$";
        if( !HasSheet( sheetname ) )
            return false;

        TESTHR(m_pRecord.CreateInstance(__uuidof(Recordset)));    

        std::stringstream stream;
        stream << "SELECT * FROM [" << sheetname << "]";

        TESTHR(m_pRecord->Open(stream.str().c_str(), connStr, adOpenStatic, adLockOptimistic, adCmdText));

    }
    catch(_com_error &e)
    {        
        _bstr_t bstrDescription(e.Description());      
        CharToOem(bstrDescription, bstrDescription);
        std::cout << bstrDescription << std::endl;
    }
    return true;
}

_variant_t *XlsReader::GetValue( std::string valuename )
{
    const char *namestr = valuename.c_str();

    for( long i=0; i<m_pRecord->Fields->GetCount(); ++i )
    {
        FieldPtr p = m_pRecord->Fields->GetItem(i);
        _bstr_t s = p->GetName();
        if( strcmp(s,  namestr )==0 )
        {
            _variant_t *v = &(p->Value);
            return v;
        }
    }
    return NULL;
}

std::string XlsReader::makeConnStr( std::string filename, bool header /*= true*/ )
{
    std::stringstream stream;

    std::string hdr = header ? "YES" : "NO";    

    if(!filename.empty())       
        if(*--filename.end() == 'x')        
            stream << "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" << filename << ";Extended Properties=\"Excel 12.0 Xml;HDR=" << hdr << "\"";                    
        else        
            stream << "Provider='Microsoft.JET.OLEDB.4.0';Data Source=" << filename << ";Extended Properties=\"Excel 8.0;HDR=" << hdr << "\"";

    return stream.str();
}

bool XlsReader::HasSheet( std::string sheetname )
{
    m_pSchema = m_pConnection->OpenSchema(adSchemaTables);      
    while( !m_pSchema->adoEOF )
    {
        std::string cursheet = (char*)(_bstr_t)m_pSchema->Fields->GetItem("TABLE_NAME")->Value.bstrVal;
        if( cursheet == sheetname )
            return true;
        m_pSchema->MoveNext();
    }
    return false;
}

bool XlsReader::NextRow()
{
    if( m_pRecord )
    {
        m_pRecord->MoveNext();
        if( !Eof() )
            return true;
    }
    return false;
}

int XlsReader::GetInt32Value( std::string valuename , int defaultvalue)
{
    _variant_t *v = GetValue(valuename);
    if( v )
    {
        return (int)v->dblVal;
    }
    return defaultvalue;
}

std::string XlsReader::GetStringValue( std::string valuename,std::string defaultvalue /*= "" */ )
{
    _variant_t *v = GetValue(valuename);
    if( v )
    {
        return (char*)(_bstr_t)v->bstrVal;;
    }
    return defaultvalue;
}

float XlsReader::GetFloatValue( std::string valuename, float defaultvalue /*= 0.0f */ )
{
    _variant_t *v = GetValue(valuename);
    if( v )
    {
        return (float)v->dblVal;
    }
    return defaultvalue;
}

void XlsReader::CleanUp()
{
    if( m_pRecord )
        m_pRecord->Close();
    if( m_pSchema )
        m_pSchema->Close();
    if( m_pConnection )
        m_pConnection->Close();
    m_pRecord = NULL;
    m_pSchema = NULL;
    m_pConnection = NULL;
    ::CoUninitialize();
}
